﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Workflow.Runtime;
using ApplicationGenerator.CommonModule;
using ApplicationGenerator.DataAccess;
using ApplicationGenerator.Versions.BusinessLogic;

namespace WCFS
{
    // NOTE: If you change the class name "Service1" here, you must also update the reference to "Service1" in Web.config and in the associated .svc file.
    public class ProcessService : IServiceContract
    {
        #region Variables
        private static string serviceConfigFilePath = @"d:\service_config.xml";//System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase) + @"service_config.xml";

        private static WorkflowRuntime workflowRuntime = new WorkflowRuntime();
        private static Dictionary<KeyValuePair<Guid, int>, WorkflowInstance> instances = new Dictionary<KeyValuePair<Guid, int>, WorkflowInstance>();
        private static Dictionary<KeyValuePair<Guid, int>, VersionInfo> actualDisplayedPages = new Dictionary<KeyValuePair<Guid, int>, VersionInfo>();
        private static WorkflowLibrary.WorkflowPropertyTrackingService trackingService = new WorkflowLibrary.WorkflowPropertyTrackingService();

        #endregion

        #region Construction

        public ProcessService()
        {
        }

        static ProcessService()
        {
            bool connectionStringFound = false;

            XmlConfigMaker config = new XmlConfigMaker(serviceConfigFilePath);
            List<ConfigEntry> list = config.GetAllEntries();
            foreach (ConfigEntry entry in list)
            {
                if (entry.Name == "connectionstring")
                {
                    ApplicationGenerator.DataAccess.DesignerDataBaseManager.Instance.ConnectionString = entry.Value;
                    connectionStringFound = true;
                    break;
                }
            }
            if (false == connectionStringFound)
            {
                throw new WCFServiceException("could not found connectionstring tag in configuration file.");
            }

            try
            {
                workflowRuntime.AddService(trackingService);
            }
            catch (Exception ex)
            {
                throw new WCFServiceException("could not add tracking service to WCFS.");
            }
        }

        #endregion

        #region Contract methods

        /// <summary>
        /// This method is responsible for creating new workflow, adding it to map, managing it's suspend
        /// event
        /// </summary>
        /// <param name="_workflowType">Determines workflow type</param>
        /// <param name="_instanceId">generator istance id</param>
        /// <returns>Guid of created workflow</returns>
        public Guid CreateWorkflow(Guid _workflowType, int _instanceId)
        {
            WorkflowInstance instance = null;
            AutoResetEvent waitHandle = new AutoResetEvent(false);
            Dictionary<string, object> properties = new Dictionary<string, object>();
            KeyValuePair<Guid, int> key = new KeyValuePair<Guid, int>(_workflowType, _instanceId);
            properties["InstanceId"] = _instanceId;


            if (true == instances.ContainsKey(key))
            {
                throw new WCFServiceException("same process already exists. (type: " + _workflowType + ", instanceId: " + _instanceId);
            }

            workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) { waitHandle.Set(); };
            workflowRuntime.WorkflowSuspended += delegate(object sender, WorkflowSuspendedEventArgs e)
            {
                //get the custom tracking service and retrieve the most recent values for the workflow properties
                Dictionary<string, object> wfParams =
                    workflowRuntime.GetService<WorkflowLibrary.WorkflowPropertyTrackingService>()
                    .GetWorkflowProperties(e.WorkflowInstance.InstanceId);
            };

            workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)
            {
                waitHandle.Set();
            };

            instance = workflowRuntime.CreateWorkflow(
                ((WCFS.WorkflowFactory.getInstance(serviceConfigFilePath)).GetWorkflowType(
                ApplicationGenerator.DataAccess.DesignerDataBaseManager.Instance.GetName(_workflowType, Policy.Workflow))),
                properties);

            instance.Start();

            instances.Add(key, instance);

            //do not wait for this process
            waitHandle.Set();

            return instance.InstanceId;
        }

        /// <summary>
        /// This method is responsible for getting from current activity which page should be 
        /// displayed
        /// </summary>
        /// <param name="_workflowInstanceId">generator workflow instance id</param>
        /// <returns>guid of page to be displayed</returns>
        public VersionInfo GetPageToDisplay(Guid _workflowType, int _workflowInstanceId)
        {
            KeyValuePair<Guid, int> _identifier = new KeyValuePair<Guid, int>(_workflowType, _workflowInstanceId);

            if (false == instances.ContainsKey(_identifier))
            {
                throw new WCFServiceException("nothing to be displayed - process does not exist. (type: " + _workflowType + ", instanceId: " + _workflowInstanceId);
            }

            WorkflowInstance winstance = instances[_identifier];

            if (winstance != null)
            {
                Dictionary<string, object> wfParams =
                            workflowRuntime.GetService<WorkflowLibrary.WorkflowPropertyTrackingService>()
                            .GetWorkflowProperties(winstance.InstanceId);
                if (wfParams == null)
                {
                    return null;
                }

                VersionInfo newPageToDisplay = ((VersionInfo)wfParams["PageVersionInfo"]);

                actualDisplayedPages[_identifier] = newPageToDisplay;
                return newPageToDisplay;

            }
            else
            {
                throw new WCFServiceException("could not find this kind of process (probalby null instance).");
            }

        }

        /// <summary>
        /// Determinuje czy strona powinna zostac wyswietlona czy nie.
        /// </summary>
        /// <param name="workflowInstanceId"></param>
        /// <returns></returns>
        public PROCESS_STATE IsNewPageToDisplay(Guid _workflowType, int _workflowInstanceId)
        {
            KeyValuePair<Guid, int> _identifier = new KeyValuePair<Guid, int>(_workflowType, _workflowInstanceId);
            WorkflowInstance winstance = instances[_identifier];

            if (winstance != null)
            {
                Dictionary<string, object> wfParams =
                            workflowRuntime.GetService<WorkflowLibrary.WorkflowPropertyTrackingService>()
                            .GetWorkflowProperties(winstance.InstanceId);

                //jesli nie udalo sie pobrac informacji o procesie - to go zakoncz - info to weba
                if (wfParams == null)
                {
                    return PROCESS_STATE.PROCESS_TO_BE_STOPPED;
                }

                VersionInfo newPageToDisplay = ((VersionInfo)wfParams["PageVersionInfo"]);

                //jesli nowa strona ma typ: koniec procesu - to wyslij ta informacje do weba
                if (newPageToDisplay.Type == VersionElement.ProcessStopped)
                {
                    return PROCESS_STATE.PROCESS_TO_BE_STOPPED;
                }

                //Potrzebujemy nawet kilka razy wyświetlić ten sam formularz/listę w ramach jednego workflow
                if (true == actualDisplayedPages.ContainsKey(_identifier) &&
                     newPageToDisplay == actualDisplayedPages[_identifier])
                {
                    return PROCESS_STATE.PROCESS_NOT_READY_YET;
                }
                else
                {
                    return PROCESS_STATE.PROCESS_READY;
                }
            }
            else
            {   //jak nie udalo sie pobrac info o procesie - to wyslij informacje do weba 
                return PROCESS_STATE.PROCESS_TO_BE_STOPPED; ;
            }
        }

        public void GoAhead(Guid _workflowType, int _workflowInstanceId)
        {
            KeyValuePair<Guid, int> _identifier = new KeyValuePair<Guid, int>(_workflowType, _workflowInstanceId);
            if (false == instances.ContainsKey(_identifier))
            {
                throw new WCFServiceException("nothing to resume (workflowType: " + _workflowType + ", instanceId: " + _workflowInstanceId);
            }

            WorkflowInstance winstance = instances[_identifier];
            if (winstance != null)
            {
                winstance.Resume();
            }


        }

        #endregion
    }
}
